/**
*  \file sd24xx256.c
*
*  \brief
*
*  \author Michael J. Hohmann  <michael.hohmann@linde-mh.de>
*
*
*/

/*===============================================[ private includes  ]================================================*/
#include "sd24xx256_sim.h"
#include "../assertor/assertor.h"
#include "../pal/pal_mem.h"
#include "../mlib/min.h"
#include "../systime/systime.h"

/*===============================================[ private defines   ]================================================*/
/** configure the control byte
*
* A control byte is the first byte received following the
* Start condition from the master device.
* The control byte consists of a 4-bit control code. For the
* 24XX256, this is set as ��1010�� binary for read and write
* operations. The next three bits of the control byte are
* the Chip Select bits (A2, A1, A0). The Chip Select bits
* allow the use of up to eight 24XX256 devices on the
* same bus and are used to select which device is
* accessed. The Chip Select bits in the control byte must
* correspond to the logic levels on the corresponding A2,
* A1 and A0 pins for the device to respond. These bits
* are, in effect, the three Most Significant bits of the word
* address.
*
* The Chip Select bits A2, A1 and A0 can be used to
* expand the contiguous address space for up to 2 Mbit
* by adding up to eight 24XX256s on the same bus. In
* this case, software can use A0 of the control byte as
* address bit A15; A1 as address bit A16; and A2 as
* address bit A17. It is not possible to sequentially read
* across device boundaries.
*
*    | 1 | 0 | 1 | 0 |A17|A16|A15|R/W|
*
*/
#define CNTRL( adr, read )  ((((((U32)adr)&0x38000)>>14)&0x000E)|0xA0|read)


#define PAGE_SIZE 0x40
#define PAGE_MASK (PAGE_SIZE-1)


#define BYTE0( arg )       ( (U8) ( ( ((U32)arg)          ) & 0xff) )
#define BYTE1( arg )       ( (U8) ( ( ((U32)arg) >> 8     ) & 0xff) )
#define BYTE2( arg )       ( (U8) ( ( ((U32)arg) >> 16    ) & 0xff) )
#define BYTE3( arg )       ( (U8) ( ( ((U32)arg) >> 24    ) & 0xff) )
#define BYTEN( arg, pos )  ( (U8) ( ( ((U32)arg) >> (pos*8) & 0xff) )

/*===============================================[ private datatypes ]================================================*/

/*===============================================[ private variables ]================================================*/

/*===============================================[ private functions ]================================================*/
static void default_write_protection( BO protect );
static U32 default_time_func( struct sd24xx256_obj *me );

static RC read   ( struct sd *dev, void *  dest ,  sd_address_t src,  sd_size_t size );
static RC write  ( struct sd *dev, sd_address_t dest, void const* src,  sd_size_t size );
static RC set    ( struct sd *dev, sd_address_t dest, U8 value,  sd_size_t size );
static RC compare( struct sd *dev, void* a, sd_address_t b, sd_size_t *psize  );
static RC delay  ( struct sd *dev );
static RC copy   ( struct sd *dev, sd_address_t a, sd_address_t b, sd_size_t size  );
/*================================================[ inline functions ]================================================*/
STATIC_INLINE void timer_start(struct sd24xx256_obj *me)
{
   me->start_time=default_time_func(me);
}

STATIC_INLINE BO timeout(struct sd24xx256_obj *me)
{
   return ( default_time_func(me) - me->start_time ) > me->timeout;
}

/*================================================[ public functions ]================================================*/

RC sd24xx256_construct_sim( struct sd24xx256_obj *me, i2cgi_transmit_func_t i2c_func, sd24xx_256_wp_func_t wp_func , sd24xx_256_wp_time_t time_func, U32 timeout )
{
   ASSERTOR_PRE( NIL != me,"NULL is not allowed as object pointer." );
   ASSERTOR_PRE( NIL != i2c_func,"NULL is not allowed as i2c function pointer." );

   pal_memzero( me, sizeof(struct sd24xx256_obj) );
   me->i2c_transmit=i2c_func;


   if( NIL == wp_func )
   {
       me->write_protection=default_write_protection;
   }
   else
   {
       me->write_protection=wp_func;
       me->write_protection(TRUE); /* set write protection */
   }

   me->time_get=time_func;
   me->timeout=timeout;

   /* init virtual function table */
   me->sd.read=read;
   me->sd.write=write;
   me->sd.set=set;
   me->sd.compare=compare;
   me->sd.copy=copy;
   me->sd.delay=delay;

   return RC_SUCCESS;
}

/** Read Data
*
*  \param dev Object Pointer
*  \param dest Destination Pointer
*  \param src  Source Pointer
*  \param size Number of bytes to be read
*
*  \return always RC_SUCCESS
*/
static RC read(  struct sd *dev, void *  dest ,  sd_address_t src,  sd_size_t size )
{
    struct sd24xx256_obj *me=(struct sd24xx256_obj *) dev;
    BO ack = FALSE;

    //if(me == NIL || dest == NIL || src == NIL || size == 0)
    if(me == NIL || dest == NIL || size == 0)
    {
        return RC_ERROR;
    }

    {   /* set read address */
        U8 data[]={BYTE1(src),BYTE0(src)};
        timer_start(me);
      do
        {
           ack = me->i2c_transmit( i2cgi_cmd_start, CNTRL(src, i2cgi_ctrl_write), data, 2);
        }
        while(!ack &&  !timeout(me));
    }

    if(ack)
    {   /*  read datas */
       ack = me->i2c_transmit( i2cgi_cmd_start_stop, CNTRL(src,i2cgi_ctrl_read), dest, size);
    }

    if(ack)
    {
       return RC_SUCCESS;
    }
    return RC_ERROR;
}

static BO set_page( struct sd24xx256_obj *me, U32 dest_adr, U8 data, sd_size_t size )
{
   ASSERTOR_PRE( NIL!=me,"");
   ASSERTOR_PRE( size <= PAGE_SIZE,"");
   ASSERTOR_PRE( ((dest_adr&(PAGE_SIZE-1))+size) <= PAGE_SIZE,"");
   {
      BO rv=FALSE;

      if(size)
      {
         me->write_protection(FALSE); /* remove write protection */
         {
            timer_start(me);
            do
            {
               U8 data[]={BYTE1(dest_adr),BYTE0(dest_adr)};
               rv=me->i2c_transmit( i2cgi_cmd_start,CNTRL(dest_adr,i2cgi_ctrl_write), data, 2);
            }while( !rv && !timeout(me) );

            while(size-- && rv )
            {
               if(size)
               rv = me->i2c_transmit( i2cgi_cmd_continue, 0, &data , 1 );
            }
         }
         me->write_protection(TRUE); /* set write protection */
      }
      return rv;
   }
}

static BO write_page( struct sd24xx256_obj *me, U32 dest_adr, void const*src, sd_size_t size )
{
   ASSERTOR_PRE( NIL!=me,"Object pointer can't be NIL");
   ASSERTOR_PRE( (NIL!=src)||(0==size),"src pointer can't be NIL with size unequal to zero");
   ASSERTOR_PRE( size <= PAGE_SIZE,"");
   ASSERTOR_PRE( ((dest_adr&(PAGE_MASK))+size) <= PAGE_SIZE,"");
   {
      BO rv=FALSE;
      if(size)
      {
         me->write_protection(FALSE); /* remove write protection */
         {
            {  /* write data address */
               timer_start(me);
               do
               {
                  U8 data[]={BYTE1(dest_adr),BYTE0(dest_adr)};
                  rv=me->i2c_transmit( i2cgi_cmd_start,CNTRL(dest_adr,i2cgi_ctrl_write), data, 2);
               }while( !rv && !timeout(me) );
            }
            if( rv )
            {  /* write data */
               rv=me->i2c_transmit( i2cgi_cmd_stop, CNTRL(dest_adr, i2cgi_ctrl_write), (void*)src , size );
            }
         }
         me->write_protection(TRUE); /* set write protection */
      }
      return rv;
   }
}
/** Write Data
*
*  \param dev Object Pointer
*  \param dest Destination Pointer
*  \param src  Source Pointer
*  \param size Number of bytes to be written
*
*  \return always RC_SUCCESS
*/
static RC write( struct sd *dev, sd_address_t dest, void const* src,  sd_size_t size )
{
   ASSERTOR_PRE(  NIL != dev,"Object pointer can't be NIL" );
   ASSERTOR_PRE( (NIL != src)||(size==0),"src pointer can't be NIL with size unequal to zero" );
   {
      struct sd24xx256_obj *me=(struct sd24xx256_obj *) dev;
      U32 dest_adr=(U32)dest;
      BO ack = TRUE;
      U8 const *p_src=src;
      U16 page_size= PAGE_SIZE -(dest_adr & ( PAGE_MASK ) );

      page_size = min2_u16( page_size, size);

      /* write first page (might not aligned with page borders) */
      ack = write_page(me, dest_adr,p_src,page_size);

      size     -= page_size;
      dest_adr += page_size;
      p_src      += page_size;

      /* write other pages (start is now aligned) */
      while(size && ack )
      {
         page_size = min2_u16( PAGE_SIZE, size);

         ack = write_page( me, dest_adr, p_src, page_size );

         size     -= page_size;
         dest_adr += page_size;
         p_src    += page_size;
      }

      if(ack)
      {
         return RC_SUCCESS;
      }
      return RC_ERROR;
   }

}

/** Copy function type.*/
static RC copy(struct sd *me, sd_address_t a, sd_address_t b, sd_size_t size  )
{
    return RC_ERROR;
}

/** Memset
*
*  The function sdspiee_set() will fill a memory area with one value.
*
*  \param dev Object Pointer
*  \param dest Destination Pointer
*  \param value Databyte to fill the memory area.
*  \param size Number of bytes to be filled with.
*
*  \return always RC_SUCCESS
*/
static RC set( struct sd *dev, sd_address_t dest, U8 value,  sd_size_t size )
{
    struct sd24xx256_obj *me=(struct sd24xx256_obj *) dev;
    U32 dest_adr=(U32)dest;
    BO ack = TRUE;
    U16 page_size= PAGE_SIZE -(dest_adr & ( PAGE_MASK ) );

    page_size = min2_u16( page_size, size);

    /* write first page (might not aligned with page borders) */
    ack = set_page(me, dest_adr,value,page_size);

    size     -= page_size;
    dest_adr += page_size;

    /* set other pages (start is now aligned) */
    while(size && ack )
    {
       page_size = min2_u16( PAGE_SIZE, size);

       ack = set_page( me, dest_adr, value, page_size );

       size     -= page_size;
       dest_adr += page_size;
    }

    if(ack)
    {
       return RC_SUCCESS;
    }
    
    return RC_ERROR;
}

static RC compare(struct sd *me, void* a, sd_address_t b, sd_size_t *psize  )
{
    return RC_ERROR;
}

static RC delay(struct sd *me)
{
    return RC_ERROR;
}

static void default_write_protection( BO protect )
{
    UNREFERENCED( protect);
}

static U32 default_time_func( struct sd24xx256_obj *me )
{
   if(NIL==me->time_get)
   {
      me->time_count++;
   }
   else
   {
      me->time_count = me->time_get();
   }
   return me->time_count;

}


/*===============================================[ private functions ]================================================*/


/***********************************************************************************************************************
*                             Copyright 2020 Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/
